/*
 * Copyright (c) 2023-2025 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.starter.excel.aop;

import cn.idev.excel.FastExcel;
import jakarta.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.util.List;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.elsfs.cloud.starter.excel.annotation.RequestExcel;
import org.elsfs.cloud.starter.excel.converters.DictTypeConvert;
import org.elsfs.cloud.starter.excel.converters.InstantConverter;
import org.elsfs.cloud.starter.excel.converters.LocalDateStringConverter;
import org.elsfs.cloud.starter.excel.converters.LocalDateTimeStringConverter;
import org.elsfs.cloud.starter.excel.converters.LocalTimeStringConverter;
import org.elsfs.cloud.starter.excel.converters.LongStringConverter;
import org.elsfs.cloud.starter.excel.converters.StringArrayConverter;
import org.elsfs.cloud.starter.excel.converters.ZonedDateTimeStringConverter;
import org.elsfs.cloud.starter.excel.handler.DictCacheClearAnalysisEventListener;
import org.elsfs.cloud.starter.excel.handler.ListAnalysisEventListener;
import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartRequest;

/** 上传excel 解析注解 */
@Slf4j
public class RequestExcelArgumentResolver implements HandlerMethodArgumentResolver {

  @Override
  public boolean supportsParameter(MethodParameter parameter) {
    return parameter.hasParameterAnnotation(RequestExcel.class);
  }

  @Override
  @SneakyThrows(Exception.class)
  public Object resolveArgument(
      MethodParameter parameter,
      ModelAndViewContainer modelAndViewContainer,
      NativeWebRequest webRequest,
      WebDataBinderFactory webDataBinderFactory) {
    Class<?> parameterType = parameter.getParameterType();
    if (!parameterType.isAssignableFrom(List.class)) {
      throw new IllegalArgumentException(
          "Excel upload request resolver error, @RequestExcel parameter is not List "
              + parameterType);
    }

    // 处理自定义 readListener
    RequestExcel requestExcel = parameter.getParameterAnnotation(RequestExcel.class);
    assert requestExcel != null;
    Class<? extends ListAnalysisEventListener<?>> readListenerClass = requestExcel.readListener();
    ListAnalysisEventListener<?> readListener = BeanUtils.instantiateClass(readListenerClass);
    // 获取请求文件流
    HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
    assert request != null;
    InputStream inputStream;
    if (request instanceof MultipartRequest) {
      MultipartFile file = ((MultipartRequest) request).getFile(requestExcel.fileName());
      assert file != null;
      inputStream = file.getInputStream();
    } else {
      inputStream = request.getInputStream();
    }

    // 获取目标类型
    Class<?> excelModelClass = ResolvableType.forMethodParameter(parameter).getGeneric(0).resolve();

    // 这里需要指定读用哪个 class 去读，然后读取第一个 sheet 文件流会自动关闭
    FastExcel.read(inputStream, excelModelClass, readListener)
        .registerConverter(LocalDateStringConverter.INSTANCE)
        .registerConverter(LocalDateTimeStringConverter.INSTANCE)
        .registerConverter(LocalTimeStringConverter.INSTANCE)
        .registerConverter(LongStringConverter.INSTANCE)
        .registerConverter(StringArrayConverter.INSTANCE)
        .registerConverter(DictTypeConvert.INSTANCE)
        .registerConverter(InstantConverter.INSTANCE)
        .registerConverter(ZonedDateTimeStringConverter.INSTANCE)
        .registerReadListener(new DictCacheClearAnalysisEventListener())
        .ignoreEmptyRow(requestExcel.ignoreEmptyRow())
        .sheet()
        .headRowNumber(requestExcel.headRowNumber())
        .doRead();

    // 校验失败的数据处理 交给 BindResult
    WebDataBinder dataBinder =
        webDataBinderFactory.createBinder(webRequest, readListener.getErrors(), "excel");
    ModelMap model = modelAndViewContainer.getModel();
    model.put(BindingResult.MODEL_KEY_PREFIX + "excel", dataBinder.getBindingResult());

    return readListener.getList();
  }
}
